package com.geeguo.ebuilder.client.business.system.organization.service.impl;

import com.geeguo.ebuilder.client.base.core.constants.VirtualConstants;
import com.geeguo.ebuilder.client.base.database.annotation.TDS;
import com.geeguo.ebuilder.client.base.redis.utils.CacheKeyFactory;
import com.geeguo.ebuilder.client.base.tenant.model.DataContext;
import com.geeguo.ebuilder.client.business.system.organization.mapper.OrganizationMapper;
import com.geeguo.ebuilder.client.business.system.organization.model.*;
import com.geeguo.ebuilder.client.business.system.organization.service.OrganizationService;
import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.ObjectEqualsHelper;
import com.geeguo.ebuilder.core.common.utils.SqlHelper;
import com.geeguo.ebuilder.core.common.utils.key.UUIDCheckUtils;
import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import com.geeguo.ebuilder.core.redis.constants.CacheTimeConstants;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class OrganizationServiceImpl implements OrganizationService {

    @Autowired
    private RedisCacheService redisCacheService;
    @Autowired
    private OrganizationMapper organizationMapper;

    @TDS
    @Override
    public List<OrganizationVO> list(DataContext dataContext) {
        List<OrganizationVO> result = new ArrayList<>();
        List<OrganizationEntity> dataList = organizationMapper.list(dataContext.getTenantCode());
        if (dataList != null && !dataList.isEmpty()) {
            Map<String, OrganizationVO> dataMapping = new HashMap<>();
            for (OrganizationEntity item : dataList) {
                OrganizationVO organization = OrganizationConverter.INSTANCE.convert2VO(item);
                if (UUIDCheckUtils.check(organization.getParentId())) {
                    OrganizationVO parent = dataMapping.get(organization.getParentId());
                    if (parent != null) {
                        parent.addChild(organization);
                    }
                } else {
                    result.add(organization);
                }
                dataMapping.put(organization.getId(), organization);
            }
        }
        return result;
    }

    @TDS
    @Override
    public List<OrgTreeNodeVO> listValidTreeNode(DataContext dataContext) {
        List<OrgTreeNodeVO> treeNodeList = new ArrayList<>();
        List<OrganizationEntity> orgList = organizationMapper.listAllValid(dataContext.getTenantCode());
        if (orgList != null && !orgList.isEmpty()) {
            Map<String, OrgTreeNodeVO> treeNodeMapping = new HashMap<>();
            for (OrganizationEntity entity : orgList) {
                OrgTreeNodeVO treeNode = new OrgTreeNodeVO();
                treeNode.setId(entity.getId());
                treeNode.setName(entity.getName());
                if (UUIDCheckUtils.check(entity.getParentId()) && treeNodeMapping.containsKey(entity.getParentId())) {
                    treeNodeMapping.get(entity.getParentId()).addChild(treeNode);
                } else {
                    treeNodeList.add(treeNode);
                }
                treeNodeMapping.put(treeNode.getId(), treeNode);
            }
        }
        return treeNodeList;
    }

    @TDS
    @Override
    public OrganizationEntity get(String id, DataContext dataContext) {
        return organizationMapper.selectById(id);
    }

    @TDS
    @Override
    public OrganizationEntity getByName(String parentId, String name, DataContext dataContext) {
        return organizationMapper.getByName(parentId, name, dataContext.getTenantCode());
    }

    @TDS
    @Override
    public OrganizationEntity getByNames(String names, DataContext dataContext) {
        OrganizationEntity entity = null;
        String[] nameArray = StringUtils.split(names, "-");
        if (nameArray != null && nameArray.length > 0) {
            String parentId = VirtualConstants.PARENT_ID;
            for (int i = 0; i < nameArray.length; i++) {
                entity = getByName(parentId, nameArray[i], dataContext);
                if (entity == null) {
                    break;
                }
                parentId = entity.getId();
            }
        }
        return entity;
    }

    @TDS
    @Override
    public OrganizationCO getCached(String id, DataContext dataContext) {
        OrganizationCO result = null;
        String cacheKey = CacheKeyFactory.INSTANCE.getOrganization(id, dataContext);
        String cacheJson = redisCacheService.get(cacheKey);
        if (StringUtils.isNotEmpty(cacheJson)) {
            result = GsonUtils.fromJson(cacheJson, OrganizationCO.class);
        }
        if (result == null) {
            OrganizationEntity entity = this.get(id, dataContext);
            if (entity != null) {
                result = OrganizationConverter.INSTANCE.convert2CO(entity);
                cacheJson = GsonUtils.toJson(result);
                redisCacheService.set(cacheKey, cacheJson, CacheTimeConstants.CACHE_WEEK);
            }
        }
        return result;
    }

    @TDS
    @Override
    public List<String> listChildrenIds(String id, DataContext dataContext) {
        OrganizationCO co = this.getCached(id, dataContext);
        List<String> result = new ArrayList<>();
        if (co != null) {
            List<String> ids = organizationMapper.listChildrenIds(SqlHelper.getSuffixImplict(co.getCode()), dataContext.getTenantCode());
            if (CollectionUtils.isNotEmpty(ids)) {
                result.addAll(ids);
            }
        }
        return result;
    }

    @TDS
    @Override
    public String getFullPath(String id, DataContext dataContext) {
        List<String> levelNames = new ArrayList<>();
        return getFullPath(id, levelNames, dataContext);
    }

    @TDS
    @Override
    public String generateCode(String parentId, DataContext dataContext) {
        OrganizationEntity parent = null;
        if (!VirtualConstants.PARENT_ID.equals(parentId)) {
            parent = get(parentId, dataContext);
            if (parent == null) {
                return null;
            }
        }
        int curIndex = 0;
        String parentCode = parent == null ? "" : parent.getCode();
        int parentDepth = parent == null ? 0 : parent.getDepth();
        String maxCode = organizationMapper.getMaxCode(SqlHelper.getSuffixImplict(parentCode), parentDepth + 1, dataContext.getTenantCode());
        if (StringUtils.isNotEmpty(maxCode)) {
            String currentCode = maxCode.substring(parentCode.length());
            curIndex = Integer.parseInt(currentCode);
        }
        return parentCode + String.format("%05d", curIndex + 1);
    }

    @TDS
    @Override
    public int getMaxSequence(String parentId, DataContext dataContext) {
        Integer result = organizationMapper.getMaxSequence(parentId, dataContext.getTenantCode());
        return result != null ? result : 0;
    }

    @TDS
    @Override
    public boolean save(OrganizationEntity entity, DataContext dataContext) {
        return organizationMapper.insert(entity) > 0;
    }

    @TDS
    @Override
    public boolean update(OrganizationEntity current, OrganizationEntity old, DataContext dataContext) {
        Map<String, Object> changeValues = ObjectEqualsHelper.equals(current, old, new String[] {"parentId", "name", "sequence", "state"});
        if (changeValues.isEmpty()) {
            return true;
        } else {
            changeValues.put("id", current.getId());
            if (changeValues.containsKey("parentId")) {
                changeValues.put("code", generateCode(current.getParentId(), dataContext));
                if (!UUIDCheckUtils.check(current.getParentId())) {
                    changeValues.put("depth", 1);
                } else {
                    changeValues.put("depth", getCached(current.getParentId(), dataContext).getDepth() + 1);
                }
            }
            changeValues.put("modifyTime", current.getModifyTime());
            changeValues.put("modifyUser", current.getModifyUser());
            boolean result = organizationMapper.updateByParams(changeValues) == 1;
            if (result) {
                removeCached(current.getId(), dataContext);
            }
            if (changeValues.containsKey("code")) {
                updateChildrenCode(old.getCode(), current.getModifyTime(), current.getModifyUser(), dataContext);
            }
            return result;
        }
    }

    @TDS
    @Override
    public boolean remove(String id, DataContext dataContext) {
        boolean result = organizationMapper.deleteById(id) == 1;
        if (result) {
            removeCached(id, dataContext);
        }
        return result;
    }

    @TDS
    @Override
    public boolean isUsed(String id, DataContext dataContext) {
        return organizationMapper.isUsed(id) != null;
    }

    @TDS
    @Override
    public boolean existsChild(String id, DataContext dataContext) {
        Integer result = organizationMapper.getChildCount(id, dataContext.getTenantCode());
        return result > 0;
    }

    private void removeCached(String id, DataContext dataContext) {
        String cacheKey = CacheKeyFactory.INSTANCE.getOrganization(id, dataContext);
        redisCacheService.del(cacheKey);
    }

    private String getFullPath(String id, List<String> levelNames, DataContext dataContext) {
        String result = null;
        OrganizationCO org = getCached(id, dataContext);
        if (org != null) {
            levelNames.add(org.getName());
            if (org.getDepth() > 1) {
                result = getFullPath(org.getParentId(), levelNames, dataContext);
            } else {
                StringBuilder levelBuilder = new StringBuilder();
                Collections.reverse(levelNames);
                for (String name : levelNames) {
                    levelBuilder.append("-").append(name);
                }
                result = levelBuilder.substring(1);
            }
        }
        return result;
    }

    private void updateChildrenCode(String oldCode, Date modifyTime, String modifyUser, DataContext dataContext) {
        List<OrganizationEntity> children = organizationMapper.listChildren(SqlHelper.getSuffixImplict(oldCode), dataContext.getTenantCode());
        if (children != null) {
            for (OrganizationEntity child : children) {
                String childCode = generateCode(child.getParentId(), dataContext);
                organizationMapper.updateCode(child.getId(), childCode, childCode.length() / 5, modifyTime, modifyUser);
                removeCached(child.getId(), dataContext);
            }
        }
    }
}